/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 
 *	            File : 	tablegen.cpp
 *       Description :  Генерация CRC-таблицы
 *
 *        				Реализовано на основе материала:
 *						Ross N. Williams - Элементарное руководство по
 *						CRC алгоритмам обнаружения ошибок
 *
 *            Create :  28.02.2013
 *            Author :  Golovchenko Aleksey (lexgolovchenko@gmail.com)
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 

#include <iomanip>
#include <fstream>
#include "tablegen.h"

CRC_table_generator::CRC_table_generator(const CRC_param &param,
										 std::ostream &cppout, bool table_only)
: CRC_model(param),
  m_out_cpp(cppout),
  m_table_only(table_only)
{

}

/*
 * Генерация CRC таблицы и функцияя расчета CRC
 */
void CRC_table_generator::generate_table()
{
	int hex_width = 0;

	/*
	 * Геренируем таблицу в *.cpp файл
	 */
	if (m_param.width == 16) {
		m_out_cpp << "const unsigned short CRCtable[] = {" << std::endl;
		hex_width = 4;
	}
	else if (m_param.width == 32) {
		m_out_cpp << "const unsigned long CRCtable[] = {" << std::endl;
		hex_width = 8;
	}
	else {
		throw CRC_bad_params();
	}

	for (int i = 1; i <= 255; ++i) {
		if (i%4 == 1) {
			m_out_cpp << "\t";
		}

		m_out_cpp << "0x" << std::setw(hex_width) << std::setfill('0')
				  << std::hex << std::uppercase << get_table_elem(i-1);

		if (i != 255) {
			m_out_cpp << ", ";
		}

		if (i%4 == 0) {
			m_out_cpp << std::endl;
		}
	}
	m_out_cpp << "\n};" << std::endl;

	/*
	 * Генерируем функцию для табличного расчета CRC
	 */
	if (!m_table_only) {
		std::string crc_type;
		if (m_param.width == 16) {
			crc_type = "unsigned short";
		}
		else {
			crc_type = "unsigned long";
		}

		m_out_cpp << "\nstatic const " << crc_type << " INIT = "
				  << "0x" << std::setw(hex_width) << std::setfill('0')
		          << std::hex << std::uppercase << m_param.init << ";" << std::endl;
		m_out_cpp << "static const " << crc_type << " XOROUT = "
						  << "0x" << std::setw(hex_width) << std::setfill('0')
				          << std::hex << std::uppercase << m_param.xorout << ";\n" << std::endl;

		m_out_cpp << crc_type << " crc_calc(unsigned char *data, int len) {\n"
				  << "\t" << crc_type << " crc = INIT;\n"
				  << "\t" << "while(len--) {" << std::endl;

		// "зеркальный" алгоритм
		if (m_param.refin && m_param.refout) {
			m_out_cpp << "\t\t" << "crc = CRCtable[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);" << std::endl;
		}

		// обычный алгоритм
		else if (!m_param.refin && !m_param.refout) {
			m_out_cpp << "\t\t" << "crc = CRCtable[((crc>>24) ^ *data++) & 0xFFL] ^ (crc << 8);" << std::endl;
		}
		else {
			throw CRC_bad_params();
		}

		m_out_cpp << "\t}\n\treturn crc ^ XOROUT;\n}\n";
  	}
}
